/*
 *
 * Copyright(c) 2010 Safelayer Secure Communications
 *
 * This is open source software, placed under the terms of the
 * GNU GENERAL PUBLIC LICENSE Version 3.
 * Please see: http://www.gnu.org/licenses/gpl-3.0.txt
 * 
 * Permission to use, copy, modify, and distribute this software
 * for any purpose and without fee is hereby granted, provided
 * that the above copyright notices appear in all copies and that
 * both the copyright notice and this permission notice appear in
 * supporting documentation.
 * 
 */
package com.neuralnoise.foaf.core;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Models a FOAF Document. The <code>Document</code> class represents those things which are, broadly
 * conceived, 'documents'.
 * <p>
 * For more info see FOAF Vocabulary Specification
 * 
 * @author victor.martinez <swtp@safelayer.com>
 */
public class Document extends Thing {

	private String sha1;

	private Set<Thing> topicSet = new LinkedHashSet<Thing>();

	private Set<Thing> primaryTopicSet = new LinkedHashSet<Thing>();

	public Document(String uri) {
		// call Thing constructor
		super(uri);
	}

	/**
	 * @return the sha1.
	 */
	public String getSha1() {
		return sha1;
	}

	/**
	 * @param sha1 The sha1 to set.
	 */
	public void setSha1(String sha1) {
		this.sha1 = sha1;
	}

	/**
	 * Returns all topics.
	 * 
	 * @return Returns all topics.
	 */
	public List<Thing> getAlltopics() {
		return new ArrayList<Thing>(topicSet);
	}

	/**
	 * Adds the specified <code>topic</code> to this {@link Document} if it is not already present.
	 * 
	 * @param topic The <code>topic</code> to be added to this <code>Document</code>.
	 * @return <tt>true</tt> if the <code>Document</code> did not already contain the specified
	 *         <code>topic</code>.
	 */
	public boolean addTopic(Thing topic) {
		return topicSet.add(topic);
	}

	/**
	 * Removes all of the topics from this {@link Document}. The topics from <code>Document</code>
	 * will be empty after this call returns.
	 */
	public void clearTopics() {
		topicSet.clear();
	}

	/**
	 * Removes the specified <code>topic</code> from this {@link Document}, if it is present. Returns
	 * <tt>true</tt> if this <code>Document</code> contained the specified <code>topic</code> (or
	 * equivalently, if this <code>Document</code> changed as a result of the call).
	 * 
	 * @param topic The <code>topic</code> to be removed from this <code>Document</code>.
	 * @return <tt>true</tt> if this <code>Document</code> contained the specified
	 *         <code>topic</code>.
	 */
	public boolean removeTopic(Thing topic) {
		return topicSet.remove(topic);
	}

	/**
	 * Returns <tt>true</tt> if this {@link Document} contains the specified <code>topic</code>.
	 * 
	 * @param topic The <code>topic</code> whose presence in this <code>Document</code> is to be
	 *            tested.
	 * @return <tt>true</tt> if this <code>Document</code> contains the specified <code>topic</code>
	 */
	public boolean containsTopic(Thing topic) {
		return topicSet.contains(topic);
	}

	/**
	 * Returns all primaryTopics.
	 * 
	 * @return Returns all primaryTopics.
	 */
	public List<Thing> getAllPrimaryTopics() {
		return new ArrayList<Thing>(primaryTopicSet);
	}

	/**
	 * Adds the specified <code>primaryTopic</code> to this {@link Document} if it is not already
	 * present.
	 * 
	 * @param primaryTopic The <code>primaryTopic</code> to be added to this <code>Document</code>.
	 * @return <tt>true</tt> if the <code>Document</code> did not already contain the specified
	 *         <code>primaryTopic</code>.
	 */
	public boolean addPrimaryTopic(Thing primaryTopic) {
		return primaryTopicSet.add(primaryTopic);
	}

	/**
	 * Removes the specified <code>primaryTopic</code> from this {@link Document}, if it is present.
	 * Returns <tt>true</tt> if this <code>Document</code> contained the specified
	 * <code>primaryTopic</code> (or equivalently, if this <code>Document</code> changed as a result
	 * of the call).
	 * 
	 * @param primaryTopic The <code>primaryTopic</code> to be removed to this <code>Document</code>.
	 * @return <tt>true</tt> if this <code>Document</code> contained the specified
	 *         <code>primaryTopic</code>.
	 */
	public boolean removePrimaryTopic(Thing primaryTopic) {
		return primaryTopicSet.remove(primaryTopic);
	}

	/**
	 * Removes all of the primaryTopics from this {@link Document}. The primaryTopics from
	 * <code>Document</code> will be empty after this call returns.
	 */
	public void clearPrimaryTopics() {
		primaryTopicSet.clear();
	}

	/**
	 * Returns <tt>true</tt> if this {@link Document} contains the specified
	 * <code>primaryTopic</code>.
	 * 
	 * @param primaryTopic The <code>primaryTopic</code> whose presence in this
	 *            <code>Document</code> is to be tested.
	 * @return <tt>true</tt> if this <code>Document</code> contains the specified
	 *         <code>primaryTopic</code>.
	 */
	public boolean containsPrimaryTopic(Thing primaryTopic) {
		return primaryTopicSet.contains(primaryTopic);
	}

	/**
	 * Determines hashCode value base on SHA1 hash value. If hash value is <code>null</code> then calls
	 * {@link Thing#hashCode()}.
	 */
	@Override
	public int hashCode() {
		if (sha1 != null) {
			final int prime = 31;
			int result = super.hashCode();
			result = prime * result + sha1.hashCode();
			return result;
		}
		else {
			// call Thing hashCode
			return super.hashCode();
		}
	}

	/**
	 * Determine whether two Documents are equal, their SHA1 hash are equal. If hash attribute
	 * is <tt>null</tt> then call {@link Thing#equals(Object)}.
	 * 
	 * @param obj The object to be compared.
	 * @return true if and only if both objects are equal.
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		Document other = (Document) obj;
		if (sha1 == null) {
			// call Thing equals
			return super.equals(obj);
		}
		else if (!sha1.equals(other.sha1))
			return false;
		return true;
	}

}